
// 13.36

#include "802_1Q_2005_procedures.h"
#include "bridge.h"
#include "port.h"
#include "assert.h"

enum
{
	UNDEFINED,
	ACTIVE,
	INACTIVE,
	LEARNING,
	DETECTED,
	NOTIFIED_TCN,
	NOTIFIED_TC,
	PROPAGATING,
	ACKNOWLEDGED,
};

// ============================================================================

const char* TopologyChange_802_1Q_2005_GetStateName (SM_STATE state)
{
	switch (state)
	{
		case ACTIVE:		return "ACTIVE";
		case INACTIVE:		return "INACTIVE";
		case LEARNING:		return "LEARNING";
		case DETECTED:		return "DETECTED";
		case NOTIFIED_TCN:	return "NOTIFIED_TCN";
		case NOTIFIED_TC:	return "NOTIFIED_TC";
		case PROPAGATING:	return "PROPAGATING";
		case ACKNOWLEDGED:	return "ACKNOWLEDGED";
		default:			return "(undefined)";
	}
}

// ============================================================================

// When this function returns a valid state (non-zero), it means it has changed one or more variables, so all state machines must be run again.
SM_STATE TopologyChange_802_1Q_2005_CheckConditions (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	// ------------------------------------------------------------------------
	// Check global conditions.
	
	if (bridge->BEGIN)
	{
		if (state == INACTIVE)
		{
			// The entry block for this state has been executed already.
			return 0;
		}
		
		return INACTIVE;
	}
	
	// ------------------------------------------------------------------------
	// Check exit conditions from each state.
	
	if (state == ACTIVE)
	{
		if (((portTree->role != PORT_ROLE_ROOT) && (portTree->role != PORT_ROLE_DESIGNATED) && (portTree->role != PORT_ROLE_MASTER)) || port->operEdge)
			return LEARNING;
		
		if (port->rcvdTcn)
			return NOTIFIED_TCN;
		
		if (portTree->rcvdTc)
			return NOTIFIED_TC;
		
		if (portTree->tcProp && !port->operEdge)
			return PROPAGATING;
		
		if (port->rcvdTcAck)
			return ACKNOWLEDGED;
		
		return 0;
	}

	if (state == INACTIVE)
	{
		if (portTree->learn && !portTree->fdbFlush)
			return LEARNING;
		
		return 0;
	}

	if (state == LEARNING)
	{
		if (((portTree->role == PORT_ROLE_ROOT) || (portTree->role == PORT_ROLE_DESIGNATED) || (portTree->role == PORT_ROLE_MASTER)) && portTree->forward && !port->operEdge)
			return DETECTED;
		
		if ((portTree->role != PORT_ROLE_ROOT) && (portTree->role != PORT_ROLE_DESIGNATED) && (portTree->role != PORT_ROLE_MASTER) && !(portTree->learn || portTree->learning) && !(portTree->rcvdTc || port->rcvdTcn || port->rcvdTcAck || portTree->tcProp))
			return INACTIVE;
		
		if (portTree->rcvdTc || port->rcvdTcn || port->rcvdTcAck || portTree->tcProp)
			return LEARNING;
		
		return 0;
	}
	
	if (state == DETECTED)
		return ACTIVE;
	
	if (state == NOTIFIED_TCN)
		return NOTIFIED_TC;
	
	if (state == NOTIFIED_TC)
		return ACTIVE;
	
	if (state == PROPAGATING)
		return ACTIVE;
	
	if (state == ACKNOWLEDGED)
		return ACTIVE;
	
	assert (false);
	return 0;
}

// ============================================================================

void TopologyChange_802_1Q_2005_InitState (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	if (state == ACTIVE)
	{
	}
	else if (state == INACTIVE)
	{
		//portTree->fdbFlush = true;
		bridge->callbacks->flushFdb (bridge, givenPort, givenTree);
		
		portTree->tcWhile = 0;
		if (givenTree == CIST_INDEX)
			port->tcAck = false;
	}
	else if (state == LEARNING)
	{
		if (givenTree == CIST_INDEX)
			portTree->rcvdTc = port->rcvdTcn = port->rcvdTcAck = false;

		portTree->rcvdTc = portTree->tcProp = false;
	}
	else if (state == DETECTED)
	{
		newTcWhile (bridge, givenPort, givenTree);
		setTcPropTree (bridge, givenPort, givenTree);
		if (givenTree == CIST_INDEX)
			port->newInfo = true;
		else
			port->newInfoMsti = true;
	}
	else if (state == NOTIFIED_TCN)
	{
		newTcWhile (bridge, givenPort, givenTree);
	}
	else if (state == NOTIFIED_TC)
	{
		if (givenTree == CIST_INDEX)
			port->rcvdTcn = false;
		portTree->rcvdTc = false;
		if ((givenTree == CIST_INDEX) && (portTree->role == PORT_ROLE_DESIGNATED))
			port->tcAck = true;
		setTcPropTree (bridge, givenPort, givenTree);
	}
	else if (state == PROPAGATING)
	{
		newTcWhile (bridge, givenPort, givenTree);
		
		//portTree->fdbFlush = true;
		bridge->callbacks->flushFdb (bridge, givenPort, givenTree);
		
		portTree->tcProp = false;
	}
	else if (state == ACKNOWLEDGED)
	{
		portTree->tcWhile = 0;
		port->rcvdTcAck = false;
	}
	else
		assert (false);
}
		